package iit.projects.etaxi.model.dao;

import iit.projects.etaxi.model.dto.SubCitySCDO;
import iit.projects.etaxi.model.entity.City;
import iit.projects.etaxi.model.entity.SubCity;
import iit.projects.etaxi.model.exception.DuplicateSubCityException;
import iit.projects.etaxi.util.Constants;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class HibernateSubCityDao extends HibernateDaoSupport implements
		SubCityDao {

	final Logger log = LoggerFactory.getLogger(HibernateSubCityDao.class);
	
	
	@Override
	public SubCity addSubCity(SubCity subCity) throws DuplicateSubCityException {
		if(log.isDebugEnabled()){
			log.debug("Going to save Sub City : " + subCity.getCode());
		}
		Session session = getSession(false);
		City city = subCity.getCity();
		try{
//			SubCityKey subCityKey = new SubCityKey();
//			subCityKey.setCity(city);
//			subCityKey.setCode(subCity.getCode());
			
			//If adding the sub city into the set fails. A subcity with the same code already exists.
			if(!city.getSubcities().add(subCity)){
				throw new DuplicateSubCityException(new String[]{subCity.getCode(), city.getCode()});
			}
			
//			SubCity findSubCity = findSubCityByName(subCity.getName());
//			if(findSubCity != null){
//				throw new DuplicateSubCityException(new String[]{subCity.getName()});
//			}			
			//session.save(subCity);
			City savedCity = (City)session.merge(city);
			
			List<SubCity> subCities = new ArrayList<SubCity>();
			subCities.addAll(savedCity.getSubcities());
			return subCities.get(subCities.indexOf(subCity));
			
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}

	@Override
	public void deleteSubCity(SubCity subCity) {
		if (log.isDebugEnabled()) {
			log.debug("Deleting Sub City " + subCity.getCode());
		}
		Session session = getSession(false);
		try {
			session.delete(subCity);
		} catch (HibernateException ex) {
			throw convertHibernateAccessException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Set<SubCity> findSubCities(SubCitySCDO subCitySCDO) {
		if(log.isDebugEnabled()){
			log.debug("Finding Sub Cities");
		}
		
		Set<SubCity> resultSet = new HashSet<SubCity>();
		Session session = getSession(false);
		
		try {
			String code = subCitySCDO.getSubCityCode();
			String name = subCitySCDO.getSubCityName();

			Criteria criteria = session.createCriteria(SubCity.class);
			
			criteria.add(Restrictions.eq("city.code", subCitySCDO.getCityCode()));
			
			if (code != null && !code.isEmpty()) {
				if (!code.contains("%")) {
					code = "%" + code + "%";
				}
				criteria.add(Restrictions.like("code", code));
			}
			if (name != null && !name.isEmpty()) {
				if (!name.contains("%")) {
					name = "%" + name + "%";
				}
				criteria.add(Restrictions.like("name", name));
			}

			criteria.addOrder(Order.asc("code"));
			List<SubCity> list = criteria.list();
			resultSet.addAll(list);
			
		} catch (HibernateException ex) {
			throw convertHibernateAccessException(ex);
		}
		return resultSet;
	}

	@Override
	public SubCity updateSubCity(SubCity subCity) {
		if (log.isDebugEnabled()) {
			log.debug("Updating Sub City " + subCity.getCode());
		}
		Session session = getSession(false);
		try {
			SubCitySCDO subCitySCDO = new SubCitySCDO();
			subCitySCDO.setCityCode("SC001");
			subCitySCDO.setSubCityCode("SC001");
			
			subCity.setSubCitiesInProximity(findSubCities(subCitySCDO));
			return (SubCity) session.merge(subCity);
		} catch (HibernateException ex) {
			throw convertHibernateAccessException(ex);
		}
	}
	
	@Override
	public Map<String, SubCity> setProximity(SubCity subCity1, SubCity subCity2){
		if (log.isDebugEnabled()) {
			log.debug("Going to set Proximity of Sub Cities " + subCity1.getCode() + ", " + subCity2.getCode());
		}
		
		//Hibernate Session.
		Session session = getSession(false);
		try {
			subCity1.getSubCitiesInProximity().add(subCity2);
			subCity2.getSubCitiesInProximity().add(subCity1);
			
			SubCity savedSub1 = (SubCity)session.merge(subCity1);
			SubCity savedSub2 = (SubCity)session.merge(subCity2);
			
			Map<String, SubCity> savedMap = new HashMap<String, SubCity>();
			savedMap.put(Constants.SELECTED_SUBCITY, savedSub1);
			savedMap.put(Constants.CLOSE_BY_SUBCITY, savedSub2);
			
			return savedMap;
			//session.saveOrUpdate(subCity2);
		} catch (HibernateException ex) {
			throw convertHibernateAccessException(ex);
		}
	}
	
	@Override
	public Map<String, SubCity> removeProximity(SubCity selectedSubCity, SubCity removingSubCity){
		if (log.isDebugEnabled()) {
			log.debug("Going remove Proximity of " + selectedSubCity.getCode() + ", " + removingSubCity.getCode());
		}
		
		selectedSubCity.getSubCitiesInProximity().remove(removingSubCity);
		removingSubCity.getSubCitiesInProximity().remove(selectedSubCity);
		
		Session session = getSession(false);
		try {
			Map<String, SubCity> savedMap = new HashMap<String, SubCity>();
			savedMap.put(Constants.SELECTED_SUBCITY, (SubCity)session.merge(selectedSubCity));
			savedMap.put(Constants.REMOVED_SUBCITY, (SubCity)session.merge(removingSubCity));
			
			return savedMap;
		} catch (HibernateException ex) {
			throw convertHibernateAccessException(ex);
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public SubCity findSubCityByName(String name)
	{
		if(log.isDebugEnabled()){
			log.debug("Going to find Driver by city name " + name);
		}
		
		Session session = getSession(false);
		
		try{
			Criteria criteria = session.createCriteria(SubCity.class);
			criteria.add(Restrictions.eq("name", name));
			
			List<SubCity> list = criteria.list();
			if(list != null && !list.isEmpty()){
				return list.iterator().next();
			}
			else{
				return null;
			}
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}

}
